/*
 * Copyright 2012 Zach Whaley
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package me.zachwhaley.android;

import me.zachwhaley.android.provider.ListContract.Lists;
import me.zachwhaley.android.provider.ListContract.Things;
import me.zachwhaley.android.ui.ThingCheckBox;
import me.zachwhaley.android.utils.AlertDialogs;
import me.zachwhaley.android.utils.DBUtils;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class ThingsFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor>
{
	final static int LOADER_ID = 1;
	
	final static private String[] PROJECTION = { Things._ID, Things.THING_NAME, Things.THING_CHECK };
	
	final static private String[] FROM = { Things.THING_CHECK, Things.THING_NAME };
	final static private int[] TO = { R.id.thing_complete, R.id.thing_name };

	private long mListID;
	private Activity mActivity;
	private Cursor mCursor;
	private CursorAdapter mAdapter;

	/**
	 * @see android.support.v4.app.Fragment#onCreate(android.os.Bundle)
	 */
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);

		setHasOptionsMenu(true);
	}

	/**
	 * @see android.support.v4.app.Fragment#onActivityCreated(android.os.Bundle)
	 */
	@Override
	public void onActivityCreated(Bundle savedInstanceState)
	{
		super.onActivityCreated(savedInstanceState);
		
		mActivity = getActivity();
		mListID = mActivity.getIntent().getLongExtra(Things.LIST_ID, -1);
		mActivity.setTitle(mActivity.getIntent().getCharSequenceExtra(Lists.LIST_NAME));
		
		// Create the cursor adapter
		setThingAdapter();
//		mAdapter = new SimpleThingAdapter(mActivity);
		// Set the list adapter
		setListAdapter(mAdapter);
		
		getListView().setChoiceMode(AbsListView.CHOICE_MODE_NONE);
		registerForContextMenu(getListView());
		
		setEmptyText("No Things...");
		setListShown(false);

		Bundle bundle = new Bundle();
		// The loader needs the list ID
		bundle.putLong(Things.LIST_ID, mListID);
		getLoaderManager().initLoader(LOADER_ID, bundle, this);
	}
	
	/**
	 * Sets up Cursor Adapter for Cursor Loader
	 * Sets View Binder to set check box
	 */
	private void setThingAdapter() 
	{

		// Creates the backing adapter for the ListView.
		mAdapter = new SimpleCursorAdapter(mActivity, R.layout.simple_thing_item, null, FROM, TO, 0);
		
		final OnCheckedChangeListener listener = new OnCheckedChangeListener() 
		{
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean checked) 
			{
				// update the database with the selection
				ContentValues values = new ContentValues();
				values.put(Things.THING_CHECK, (checked ? DBUtils.TRUE : DBUtils.FALSE));
				long id = ((ThingCheckBox) buttonView).getID();
				if (id > -1)
					updateThing(id, values);
				restartLoader();
			}
		};

		// In order to set the checked state in the checkbox
		((SimpleCursorAdapter) mAdapter).setViewBinder(new SimpleCursorAdapter.ViewBinder() 
		{
			public boolean setViewValue(View view, Cursor cursor, int columnIndex) 
			{
				if (columnIndex == cursor.getColumnIndex(Things.THING_CHECK)) 
				{
					final ThingCheckBox cb = (ThingCheckBox) view;
					cb.setOnCheckedChangeListener(null);
					
					long id = cursor.getLong(cursor.getColumnIndex(Things._ID));
					cb.setID(id);
					
					boolean checked = (cursor.getInt(cursor.getColumnIndex(Things.THING_CHECK)) == DBUtils.TRUE);
					cb.setChecked(checked);

					// Set a simple on change listener that updates the note on changes.
					cb.setOnCheckedChangeListener(listener);

					return true;
				} 
				else if (columnIndex == cursor.getColumnIndex(Things.THING_NAME)) 
				{
					TextView tv = (TextView) view;
					// Set Title grey out or not
					boolean checked = (cursor.getInt(cursor.getColumnIndex(Things.THING_CHECK)) == DBUtils.TRUE);
					tv.setEnabled(!checked);

					// Return false so the normal call is used to set the text
					return false;
				}
				return false;
			}
		});
	}

	/**
	 * @see android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader(int, android.os.Bundle)
	 */
	public Loader<Cursor> onCreateLoader(int id, Bundle bundle)
	{
		String[] where = { bundle.getLong(Things.LIST_ID) + "" };
		return new CursorLoader(mActivity, Things.CONTENT_URI, PROJECTION, Things.LIST_ID + "=?", where , null);
	}

	/**
	 * @see android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished(android.support.v4.content.Loader, java.lang.Object)
	 */
	public void onLoadFinished(Loader<Cursor> loader, Cursor cursor)
	{
		mAdapter.swapCursor(cursor);
		setListShownNoAnimation(true);
	}

	/**
	 * @see android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset(android.support.v4.content.Loader)
	 */
	public void onLoaderReset(Loader<Cursor> loader)
	{
		mAdapter.swapCursor(null);
	}
	
	/**
	 * Restarts the cursor loader causing the view to refresh
	 */
	protected void restartLoader()
	{
		Bundle bundle = new Bundle();
		// Loader needs the list ID
		bundle.putLong(Things.LIST_ID, mListID);
		getLoaderManager().restartLoader(LOADER_ID, bundle, this);
	}
	
	/**
	 * @see android.support.v4.app.ListFragment#onListItemClick(android.widget.ListView, android.view.View, int, long)
	 */
	@Override
	public void onListItemClick(ListView l, View v, int position, long id)
	{
		
	}

	/**
	 * @see android.support.v4.app.Fragment#onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater)
	 */
	@Override
	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
	{
		super.onCreateOptionsMenu(menu, inflater);
		inflater.inflate(R.menu.things_menu, menu);
	}

	/**
	 * @see android.support.v4.app.Fragment#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo)
	 */
	@Override
	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
	{
		super.onCreateContextMenu(menu, v, menuInfo);
		MenuInflater inflater = mActivity.getMenuInflater();
		inflater.inflate(R.menu.things_context_menu, menu);
	}

	/**
	 * @see android.support.v4.app.Fragment#onOptionsItemSelected(android.view.MenuItem)
	 */
	public boolean onOptionsItemSelected(MenuItem item)
	{
		// find the given menu item
		switch (item.getItemId())
		{
		case R.id.sort:
			// TODO: Sort things based on a criteria
			return true;
		case R.id.add_thing:
			addThing();
			return true;
		case R.id.delete_all:
			clearAll();
			return true;
		case R.id.clear_all:
			// TODO: Delete only checked things
			return true;
		case R.id.check_all:
			// Check or Uncheck all things
			setThingsCompleted(!areAllSelected(mListID));
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	/**
	 * @see android.support.v4.app.Fragment#onContextItemSelected(android.view.MenuItem)
	 */
	@Override
	public boolean onContextItemSelected(MenuItem item)
	{
		final long id = mAdapter.getItemId(mAdapter.getCursor().getPosition());
		// find the given context menu item
		switch (item.getItemId())
		{
		case R.id.rename_thing:
			renameThing(id);
			return true;
		case R.id.delete_thing:
			deleteThing(id);
			return true;
		case R.id.copy_thing:
			// Copy thing to another list
			copyThing(id);
			return true;
		case R.id.move_thing:
			// Move thing to another list
			moveThing(id);
			return true;
		default:
			return super.onContextItemSelected(item);
		}
	}

	/**
	 * Calls an {@link AlertDialog} with a TextView for input Renames the
	 * selected Thing
	 * 
	 * @param id
	 */
	private void renameThing(final long id)
	{
		AlertDialogs.showInputDialog(mActivity, "Rename Thing?", null, null, new AlertDialogs.InputCallBacks()
		{
			public void positiveButtonPressed(String text)
			{
				// Set updated values
				ContentValues values = new ContentValues();
				values.put(Things.THING_NAME, text);
				updateThing(id, values);
				
				// Restart the loader to reload the view
				restartLoader();
			}
			public void negativeButtonPressed(String text)
			{
			}
		});
	}

	/**
	 * Calls an {@link AlertDialog} with a list of the current Lists Copies the
	 * selected Thing to the selected List
	 * 
	 * @param id
	 *            selected Thing ID
	 */
	private void copyThing(final long id)
	{
		AlertDialogs.showListDialog(mActivity, "Copy to...", getListNames(), new DialogInterface.OnClickListener()
		{
			// If a list was chosen...
			public void onClick(DialogInterface dialog, int item)
			{
				// Get all thing values
				Thing thing = getThing(id);
				
				ContentValues values = new ContentValues();
				values.put(Things.THING_NAME, thing.getName());
				values.put(Things.THING_CHECK, 0); // Set Thing to unchecked
				// Set the list ID of the selected list to receive the Thing
				values.put(Things.LIST_ID, getListIds()[item]);
				
				// Insert the selected Thing into the selected List
				mActivity.getContentResolver().insert(Things.CONTENT_URI, values);

				// restart the loader to reload the view
				restartLoader();
				// Display a toast confirming the copied thing to the user
				Toast.makeText(mActivity,
						values.getAsString(Things.THING_NAME) + " was copied to " + getListNames()[item],
						Toast.LENGTH_SHORT).show();
			}
		});
	}

	/**
	 * Calls an {@link AlertDialog} with a list of the current lists Moves the
	 * selected Thing to the selected List
	 * 
	 * @param id
	 *            selected Thing ID
	 */
	private void moveThing(final long id)
	{
		AlertDialogs.showListDialog(mActivity, "Move to...", getListNames(), new DialogInterface.OnClickListener()
		{
			// If a list was chosen
			public void onClick(DialogInterface dialog, int item)
			{
				// Set the Thing's List
				ContentValues values = new ContentValues();
				values.put(Things.LIST_ID, getListIds()[item]);
				// Set Thing unchecked and update the edited time
				values.put(Things.THING_CHECK, 0);
				updateThing(id, values);

				// Restart the loader to reload the view
				restartLoader();

				// Display a toast confirming the moved thing.
				Thing thing = getThing(id);
				Toast.makeText(mActivity,
						thing.getName() + " was moved to " + getListNames()[item],
						Toast.LENGTH_SHORT).show();
			}
		});
	}

	private void addThing()
	{
		AlertDialogs.showInputDialog(mActivity, "New Thing", "Add", null, new AlertDialogs.InputCallBacks()
		{
			public void positiveButtonPressed(String text)
			{
				// Insert a new Thing
				ContentValues values = new ContentValues();
				values.put(Things.THING_NAME, text);
				values.put(Things.LIST_ID, mListID);
				mActivity.getContentResolver().insert(Things.CONTENT_URI, values);
				// restart the loader to reload the view
				restartLoader();
			}
			public void negativeButtonPressed(String text)
			{
			}
		});
	}

	private void deleteThing(final long id)
	{
		AlertDialogs.showAlertDialog(mActivity, "Delete Thing?", "Yes", null, new AlertDialogs.AlertCallBacks()
		{
			public void positiveButtonPressed()
			{
				// Delete the selected thing
				mActivity.getContentResolver().delete(ContentUris.withAppendedId(Things.CONTENT_URI, id), null, null);
				// restart the loader to reload the view
				restartLoader();
			}
			public void negativeButtonPressed()
			{
			}
		});
	}

	private void clearAll()
	{
		AlertDialogs.showAlertDialog(mActivity, "Clear all things?", "Yes", null, new AlertDialogs.AlertCallBacks()
		{
			public void positiveButtonPressed()
			{
				// Delete all things with the current list ID
				clearThings(mListID);
				// Restart the loader to reload the view
				restartLoader();
			}
			public void negativeButtonPressed()
			{
			}
		});
	}
	
	public String[] getListNames()
	{
		mCursor = mActivity.getContentResolver().query(Lists.CONTENT_URI, 
				new String[] { Lists.LIST_NAME }, 
				null, null, null); 
		String[] names = new String[mCursor.getCount()];
		int ndx = 0;
		while (mCursor.moveToNext())
		{
			names[ndx] = mCursor.getString(mCursor.getColumnIndexOrThrow(Lists.LIST_NAME));
			++ndx;
		}
		return names;
	}
	
	public long[] getListIds()
	{
		mCursor = mActivity.getContentResolver().query(Lists.CONTENT_URI, 
				new String[] { Lists._ID }, 
				null, null, null); 
		long[] ids = new long[mCursor.getCount()];
		int ndx = 0;
		while (mCursor.moveToNext())
		{
			ids[ndx] = mCursor.getLong(mCursor.getColumnIndexOrThrow(Lists._ID));
			++ndx;
		}
		return ids;
	}
	
	/**
	 * Performs a query for a given Thing's Name, completed status and associated List ID
	 * 
	 * @param id
	 *            Thing ID
	 * @return Thing associated with input id
	 */
	public Thing getThing(final long id)
	{
		mCursor = mActivity.getContentResolver().query(ContentUris.withAppendedId(Things.CONTENT_URI, id),
				new String[] { Things.LIST_ID, Things.THING_NAME, Things.THING_CHECK }, 
				null, null, null);
		
		Thing thing = new Thing(id);
		if (mCursor.moveToFirst())
		{
			thing.setListID(mCursor.getLong(mCursor.getColumnIndex(Things.LIST_ID)));
			thing.setName(mCursor.getString(mCursor.getColumnIndex(Things.THING_NAME)));
			thing.setComplete((mCursor.getInt(mCursor.getColumnIndex(Things.THING_CHECK)) == DBUtils.TRUE));
		}
		mCursor.close();
		return thing;
	}
	
	public int clearThings(final long listId)
	{
		return mActivity.getContentResolver().delete(Things.CONTENT_URI,
				Things.LIST_ID + "=?",
				new String[] { listId + "" });
	}
	
	/**
	 * Updates a single Thing
	 * @param id Thing ID
	 * @param values updated values as a {@link ContentValues} object
	 */
	public int updateThing(final long id, ContentValues values)
	{
		values.put(Things.THING_EDITED, "NOW");
		return mActivity.getContentResolver().update(ContentUris.withAppendedId(Things.CONTENT_URI, id), values, null, null);
	}
	
	/**
	 * Checks all things if check is true, unchecks all things otherwise
	 * 
	 * @param check
	 *            determinate boolean for checked / unchecked
	 */
	public int setThingsCompleted(boolean complete)
	{
		// Set update values
		ContentValues values = new ContentValues();
		// The database uses integers as booleans
		// Set 1 if check equals true 0 otherwise
		values.put(Things.THING_CHECK, complete ? DBUtils.TRUE : DBUtils.FALSE);
		values.put(Things.THING_EDITED, "NOW");
		return mActivity.getContentResolver().update(Things.CONTENT_URI, values, 
				Things.LIST_ID + "=?", new String[] { mListID + "" });
	}
	
	/**
	 * Returns true if at least one Thing in the list is checked, false otherwise
	 * 
	 * @return Things check status
	 */
	public boolean areAllSelected(final long listId)
	{
		// Get a cursor with all Thing checks for the List
		mCursor = mActivity.getContentResolver().query(Things.CONTENT_URI,
				new String[] { Things.THING_CHECK }, Things.LIST_ID + "=?",
				new String[] { listId + "" }, null);

		int thingCheckIndex = mCursor.getColumnIndex(Things.THING_CHECK);
		// if the cursor is not empty
		try
		{
			while (mCursor.moveToNext())
			{
				// If at least one thing is unchecked return false
				if (mCursor.getInt(thingCheckIndex) == 0)
					return false;
			}
			return true;
		}
		// Must close cursor
		finally { mCursor.close(); }
	}
}
